#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：auto-test 
@File    ：ObjectMap.py
@Author  ：赵加响
@Date    ：2022/12/16 10:17 
@Desc    ：
"""
import os
import time

from selenium.common.exceptions import ElementNotVisibleException, WebDriverException, NoSuchElementException, \
    StaleElementReferenceException
from selenium.webdriver.common.keys import Keys

from common.find_img import FindImage
from config.yaml_config import GetConfig
from constants import IMAGE_PATH


class ObjectMap:
    # 获取基准地址
    url = GetConfig().get_url()

    def element_get(self, driver, locate_type, locator_expression, timeout=10, must_visible=False):
        """
        单个元素获取
        :param driver: 谷歌浏览器驱动
        :param locate_type: 定位方式类型
        :param locator_expression: 定位表达式
        :param timeout: 超时时间
        :param must_visible: 是否必须显示
        :return: 元素
        """
        # 开始时间
        start_ms = time.time() * 1000
        # 结束时间
        stop_ms = start_ms + (timeout * 1000)

        for x in range(int(timeout * 10)):
            # 查找元素
            try:
                element = driver.find_element(by=locate_type, value=locator_expression)
                # 如果元素不是必须可见的，就直接返回元素
                if not must_visible:
                    return element
                # 如果原始必须可见，则需要判断元素是否可见
                else:
                    # 判断元素是否可见
                    if element.is_displayed():
                        return element
                    else:
                        raise Exception()
            except:
                now_ms = time.time() * 1000
                if now_ms > stop_ms:
                    break

            time.sleep(0.1)
        raise ElementNotVisibleException('元素定位失败，定位方式：' + locate_type + '，定位表达式：' + locator_expression)

    def wait_for_ready_state_complete(self, driver, timeout=30):
        """

        :param driver:
        :param timeout:
        :return:
        """
        # 开始时间
        start_ms = time.time() * 1000
        # 结束时间
        stop_ms = start_ms + (timeout * 1000)

        for x in range(timeout * 10):
            try:
                # 获取页面的状态
                # execute_script执行js代码
                ready_state = driver.execute_script("return document.readyState")
            except WebDriverException:
                # 如果有Driver的问题，执行js就会失败，就直接掉过
                time.sleep(0.03)
                return True
            # 如果页面文件全部加载完成，返回True
            if ready_state == 'complete':
                time.sleep(0.01)
                return True
            else:
                now_ms = time.time() * 1000
                if now_ms >= stop_ms:
                    break
                time.sleep(0.1)
        raise Exception('打开网页时，页面元素没有加载完成')

    def element_disappear(self, driver, locate_type, locator_expression, timeout=30):
        """
        等待页面元素消时
        :param driver: 浏览器驱动
        :param locate_type: 定位方式类型
        :param locator_expression: 定位表达式
        :param timeout:
        :return:
        """
        if locate_type:
            start_ms = time.time() * 1000
            stop_ms = start_ms + (timeout * 1000)

            for x in range(timeout * 10):
                try:
                    element = driver.find_element(by=locate_type, value=locator_expression)
                    if element.is_displayed():
                        now_ms = time.time() * 1000
                        if now_ms >= stop_ms:
                            break
                        time.sleep(0.1)
                except Exception:
                    return True

            raise Exception('元素没有消失，定位方式：' + locate_type + '，定位表达式' + locator_expression)
        else:
            pass

    def element_appear(self, driver, locate_type, locator_expression, timeout=30):
        """
        等待页面元素出现
        :param driver:
        :param locate_type:
        :param locator_expression:
        :param timeout:
        :return:
        """

        if locate_type:
            start_ms = time.time() * 1000
            stop_ms = start_ms + (timeout * 1000)

            for x in range(int(timeout * 10)):
                try:
                    element = driver.find_element(by=locate_type, value=locator_expression)

                    if element.is_displayed():
                        return element
                    else:
                        raise Exception()
                except Exception:
                    now_ms = time.time() * 1000
                    if now_ms >= stop_ms:
                        break
                    time.sleep(0.1)
            raise ElementNotVisibleException('元素没有出现，定位方式：' + locate_type + '，定位表达式：' + locator_expression)

    def element_to_url(self, driver, url, locate_type_disappear=None, locate_expression_disappear=None,
                       locate_type_appear=None, locate_expression_appear=None):
        """
        跳转地址
        :param driver:浏览器驱动
        :param url:跳转的地址
        :param locate_type_disappear:等待页面元素消失的定位方式
        :param locate_expression_disappear:等待页面元素消失的定位表达式
        :param locate_type_appear:等待页面元素出现的定位方式
        :param locate_expression_appear:等待页面元素出现的定位表达式
        :return:
        """
        try:
            driver.get(self.url + url)
            # 等待页面元素加载完成
            self.wait_for_ready_state_complete(driver)
            # 等待页面元素消失
            self.element_disappear(driver, locate_type_disappear, locate_expression_disappear)
            # 等待页面元素出现
            self.element_appear(driver, locate_type_appear, locate_expression_appear)
        except Exception as e:
            print('跳转地址出现异常，异常原因：%s' % e)
            return False
        return True

    def element_is_display(self, driver, locate_type, locator_expression):
        """
        元素是否显示
        :param driver:
        :param locate_type:
        :param locator_expression:
        :return:
        """
        try:
            driver.find_element(by=locate_type, value=locator_expression)
            return True
        except NoSuchElementException:
            # 发生了NoSuchElementException异常，元素没有找到
            return False

    def element_fill_value(self, driver, locate_type, locator_expression, fill_value, timeout=30):
        """
        元素填值
        :param driver:
        :param locate_type:
        :param locator_expression:
        :param fill_value:
        :param timeout:
        :return:
        """
        # 元素必须先出现
        element = self.element_appear(driver, locate_type, locator_expression, timeout)
        try:
            # 清除输入框中的值
            element.clear()
        except StaleElementReferenceException:
            # 页面元素没有刷新出来
            self.wait_for_ready_state_complete(driver)
            time.sleep(0.06)
            element = self.element_appear(driver, locate_type, locator_expression, timeout)

            try:
                element.clear()
            except:
                pass
        except Exception:
            pass
        # 填入的值转成字符串
        if type(fill_value) is int or type(fill_value) is float:
            fill_value = str(fill_value)
        try:
            # 不是以\n结尾，直接填值
            if not fill_value.endswith('\n'):
                element.send_keys(fill_value)
                self.wait_for_ready_state_complete(driver)
            else:
                fill_value = fill_value[:-1]
                element.send_keys(fill_value)
                element.send_keys(Keys.RETURN)
                self.wait_for_ready_state_complete(driver)
        except StaleElementReferenceException:
            self.wait_for_ready_state_complete(driver)
            time.sleep(0.06)
            element = self.element_appear(driver, locate_type, locator_expression)
            element.clear()
            # 不是以\n结尾，直接填值
            if not fill_value.endswith('\n'):
                element.send_keys(fill_value)
                self.wait_for_ready_state_complete(driver)
            else:
                fill_value = fill_value[:-1]
                element.send_keys(fill_value)
                element.send_keys(Keys.RETURN)
                self.wait_for_ready_state_complete(driver)
        except Exception:
            raise Exception('元素填值失败')
        return True

    def element_click(self, driver, locate_type, locator_expression, locate_type_disappear=None,
                      locator_expression_disappear=None, locate_type_appear=None, locator_expression_appear=None,
                      timeout=30):
        """
        元素点击
        :param driver:
        :param locate_type:
        :param locator_expression:
        :param locate_type_disappear:
        :param locator_expression_disappear:
        :param locate_type_appear:
        :param locator_expression_appear:
        :param timeout:
        :return:
        """
        # 元素要可见
        element = self.element_appear(driver, locate_type, locator_expression)
        try:
            # 点击元素
            element.click()
        except StaleElementReferenceException:
            self.wait_for_ready_state_complete(driver)
            time.sleep(0.06)
            element = self.element_appear(driver, locate_type, locator_expression)
            element.click()
        except Exception as e:
            print('页面出现异常，元素不可点击', e)

        try:
            self.element_appear(driver, locate_type_appear, locator_expression_appear)
            self.element_disappear(driver, locate_type_disappear, locator_expression_disappear)
        except Exception as e:
            print('等待元素消失或者出现失败', e)
            return False
        return True

    def upload(self, driver, locate_type, locator_expression, file_path):
        """
        文件上传
        :param driver:
        :param locate_type:
        :param locator_expression:
        :param file_path:
        :return:
        """
        element = self.element_get(driver, locate_type, locator_expression)
        return element.send_keys(file_path)

    def switch_window_2_latest_handle(self, driver):
        """
        句柄切换到最近的窗口
        :param driver:
        :return:
        """
        window_handles = driver.window_handles
        driver.switch_to.window(window_handles[-1])

    def find_image_source(self, driver, image_name):
        """
        截图并在截图中查找图片
        :param driver:
        :param image_name:
        :return:
        """
        # 截图后图片保存的路径
        source_path = os.path.join(IMAGE_PATH, 'source_img')
        assert_path = os.path.join(IMAGE_PATH, 'assert_img')

        source_image_path = os.path.join(source_path, image_name)
        assert_image_path = os.path.join(assert_path, image_name)

        # 截图并保存图片
        driver.get_screenshot_as_file(source_image_path)
        time.sleep(1)
        # 在原图中查找是否由指定的图片，返回信心值
        return FindImage().get_confidence(source_image_path, assert_image_path)
